package com.cblz.bd.server.core.configure;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.json.JSONUtil;
import com.cblz.bd.common.core.constant.AdminLoginConstant;
import com.cblz.bd.common.core.entity.BaAdminLog;
import com.cblz.bd.common.core.utils.BdAdminUtil;
import com.cblz.bd.redis.spring.boot.starter.service.RedisService;
import com.cblz.bd.server.core.annotation.Log;
import com.cblz.bd.server.core.annotation.LoginLog;
import com.cblz.bd.server.core.entity.LoginParams;
import com.cblz.bd.server.core.entity.vo.BdAdminTokenVO;
import com.cblz.bd.server.core.service.BaAdminLogService;
import com.cblz.bd.server.utils.BcryptUtil;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * {@code @Name:} LogAspect
 * {@code @Description: } 切面
 * {@code @Author:} by yanjieping
 * {@code @CreateDate:} 2022/11/14 - 周一
 */
@Aspect
@Component
@RequiredArgsConstructor
public class LogAspect {

    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
    private final BaAdminLogService baAdminLogService;
    private final RedisService redisService;

    /**
     * web层切点
     */
    @Pointcut("@annotation(com.cblz.bd.server.core.annotation.Log)")
    public void webLog() {
    }

    /**
     * login切点
     */
    @Pointcut("@annotation(com.cblz.bd.server.core.annotation.LoginLog)")
    public void loginLog() {
    }

    /**
     * 其它服务环绕通知
     */
    @AfterReturning(pointcut = "webLog()")
    public Object doAfter(JoinPoint joinPoint) {
        //获取请求对象
        HttpServletRequest request = getRequest();
        String method = request.getMethod();
        String queryString = request.getQueryString();
        Object[] args = joinPoint.getArgs();
        String params = "";
        //获取请求参数集合并进行遍历拼接
        if (args.length > 0) {
            if ("POST".equals(method)) {
                Object object = args[0];
                Map map = BdAdminUtil.getKeyAndValue(object);
                params = JSONUtil.toJsonStr(map);
            } else if ("GET".equals(method)) {
                params = queryString;
            }
        }
        String sessionKey = new StringJoiner(":").add(AdminLoginConstant.SESSION.getValue()).add(StpUtil.getLoginId().toString()).add(StpUtil.getTokenValue()).toString();

        if (SaSessionCustomUtil.isExists(sessionKey)) {
            BaAdminLog baAdminLog = new BaAdminLog();

            SaSession session = SaSessionCustomUtil.getSessionById(sessionKey);
            AtomicReference<BdAdminTokenVO> userSession = new AtomicReference<>((BdAdminTokenVO) session.get(sessionKey));

            // 获取Log注解
            Log logAnnotation = getAnnotation(joinPoint);
            // 封装baAdminLog对象
            baAdminLog
                    .setAdminId(userSession.get().getId())
                    .setUsername(userSession.get().getUsername())
                    .setCreatetime(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")))
                    .setUrl(request.getRequestURI())
                    .setTitle(logAnnotation.description())
                    .setUseragent(request.getHeader("user-agent"))
                    .setIp(BdAdminUtil.getIPAddress(request))
                    .setData(params);
            log.info("BaAdmin系统「业务」日志记录----{}", JSONUtil.parse(baAdminLog));
            baAdminLogService.save(baAdminLog);

            redisService.expire(sessionKey, 1L, TimeUnit.HOURS);
        }

        return joinPoint;
    }

    /**
     * 登录环绕通知
     */
    @AfterReturning(pointcut = "loginLog()")
    public Object doLoginAfter(JoinPoint joinPoint) {
        //获取请求对象
        HttpServletRequest request = getRequest();
        Object[] args = joinPoint.getArgs();
        String params = JSONUtil.toJsonStr(BdAdminUtil.getKeyAndValue(args[0]));
        //获取请求参数集合并进行遍历拼接
        LoginParams loginParams = JSONUtil.toBean(params, LoginParams.class);
        loginParams.setPassword(BcryptUtil.encrypt(loginParams.getPassword()));

        String sessionKey = new StringJoiner(":").add(AdminLoginConstant.SESSION.getValue()).add(StpUtil.getLoginId().toString()).add(StpUtil.getTokenValue()).toString();

        if (SaSessionCustomUtil.isExists(sessionKey)) {
            BaAdminLog baAdminLog = new BaAdminLog();
            // 获取Log注解
            LoginLog logAnnotation = getLoginAnnotation(joinPoint);

            SaSession session = SaSessionCustomUtil.getSessionById(sessionKey);
            AtomicReference<BdAdminTokenVO> userSession = new AtomicReference<>((BdAdminTokenVO) session.get(sessionKey));

            // 封装baAdminLog对象
            baAdminLog
                    .setAdminId(userSession.get().getId())
                    .setUsername(userSession.get().getUsername())
                    .setCreatetime(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")))
                    .setUrl(request.getRequestURI())
                    .setTitle(logAnnotation.description())
                    .setUseragent(request.getHeader("user-agent"))
                    .setIp(BdAdminUtil.getIPAddress(request))
                    .setData(JSONUtil.toJsonStr(loginParams))
            ;
            log.info("BaAdmin系统「登录」日志记录----{}", JSONUtil.parse(baAdminLog));
            baAdminLogService.save(baAdminLog);

            redisService.expire(sessionKey, 1L, TimeUnit.HOURS);
        }
        return joinPoint;
    }

    /**
     * 获取Log方法上的注解
     */
    private Log getAnnotation(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        return method.getAnnotation(Log.class);
    }

    /**
     * 获取Log方法上的注解
     */
    private LoginLog getLoginAnnotation(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        return method.getAnnotation(LoginLog.class);
    }

    private HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getRequest();
    }
}
